Stan w programowaniu obiektowym

Zmienne instancyjne i klasowe, zasady dostępu do danych


dr inż. Aleksander Smywiński-Pohl

apohllo@agh.edu.pl

http://apohllo.pl/dydaktyka/programowanie-obiektowe

konsultacje: wtorek 15:30 - 18:00, pokój 4.61

Sonda Mars Climate Orbiter

Program strukturalny

In [ ]:
struct SpaceProbe {
    double spaceProbeSpeed;
    double spaceProbeWeight;
}

double thrustCorrectionForX(struct SpaceProbe spaceProbe){
    // operowanie na polach spaceProbeSpeed oraz spaceProbeWeight
}

double thrustCorrectionForY(struct SpaceProbe spaceProbe){
    // ...
}

double thrustCorrectionForZ(struct SpaceProbe spaceProbe){
    // ...
}

Primitive obsession

In [ ]:
class SpaceProbe {
    private double speedX;
    private double speedY;
    private double speedZ;
    private double weight;
    
    public double thrustCorrectionForX(double positionX, 
                      double positionY, double positionZ){
        //...
    }
}

Klasa Speed

In [ ]:
class Speed {
    public int value;
    public String unit;
    
    public Speed(int value, String unit){
        this.value = value;
        this.unit = unit;
    }
}
In [ ]:
Speed speed1 = new Speed(10, "km/h");
Speed speed2 = new Speed(20, "m/s");

In [ ]:
speed1.value = 20;
speed2.unit = "km/h";

In [ ]:
speed1.value = -10;
speed2.unit = "ala ma kota";

1 ulepszenie - użycie typu wyliczeniowego

In [ ]:
enum SpeedUnit {
    MS, KMH;
}
In [ ]:
class Speed {
    public int value;
    public SpeedUnit unit;
    
    public Speed(int value, SpeedUnit unit){
        this.value = value;
        this.unit = unit;
    }
}
In [ ]:
Speed speed1 = new Speed(10, SpeedUnit.KMH);
Speed speed2 = new Speed(20, SpeedUnit.MS);
System.out.println(speed1);

2 ulepszenie - użycie modyfikatora private

In [ ]:
class SpaceProbe {
    private Speed speed;
    
    public SpaceProbe(Speed speed){
        this.speed = speed;
    }
    
    public Speed getSpeed(){
        return this.speed;
    }
}
In [ ]:
SpaceProbe probe1 = new SpaceProbe(new Speed(10, SpeedUnit.KMH));

probe1.speed = new Speed(10, SpeedUnit.MS);
In [ ]:
SpaceProbe probe1 = new SpaceProbe(new Speed(10, SpeedUnit.KMH));
Speed speed1 = probe1.getSpeed();

speed1.unit = SpeedUnit.MS;

System.out.println(probe1.getSpeed().unit);

3 ulepszenie - modyfikator final - Speed jako ValueObject

In [ ]:
class Speed {
    public final int value;
    public final SpeedUnit unit;
    
    public Speed(int value, SpeedUnit unit){
        this.value = value;
        this.unit = unit;
    }
}
In [ ]:
SpaceProbe probe1 = new SpaceProbe(new Speed(10, SpeedUnit.KMH));
Speed speed1 = probe1.getSpeed();

speed1.unit = SpeedUnit.MS;

Jak działa modyfikator private?

In [ ]:
class Speed {
    private int value;
    private SpeedUnit unit;

    public Speed(int value, SpeedUnit unit){
        this.value = value;
        this.unit = unit;
    }
}
In [ ]:
class SpaceProbe {
    private Speed speed;
    
    public void accelerate(Speed delta){
        speed.value += delta.value;
    }
}
In [ ]:
class Speed {
    private int value;
    private SpeedUnit unit;

    public Speed(int value, SpeedUnit unit){
        this.value = value;
        this.unit = unit;
    }
    
    public Speed add(Speed that){
        if(this.unit == that.unit){
            return new Speed(this.value + that.value, this.unit);
        } else {
            return null;
        }
    }
    
    public boolean isLower(int value){
        return this.value < value;
    }
}
In [ ]:
class SpaceProbe {
    private Speed speed;
    private static final int MAXIMUM_SPEED = 100;

    public void accelerate(Speed delta){
        Speed newSpeed = this.speed.add(delta);
        if(newSpeed.isLower(MAXIMUM_SPEED)){
            this.speed = newSpeed;
        }
    }
}

Modyfikator protected

In [ ]:
class Speed {
    protected SpeedUnit unit;
    
    public Speed(SpeedUnit unit){
        this.unit = unit;
    }
    
    public Speed add(Speed delta){
        return this; // does nothing
    }
}
In [ ]:
class Speed1D extends Speed {
    protected int value;

    public Speed1D(int value, SpeedUnit unit){
        super(unit);
        this.value = value;
    }
    
    public Speed add(Speed delta){
        if(delta instanceof Speed1D){
            Speed1D delta1d = (Speed1D) delta;
            if(this.unit == delta1d.unit){
                return new Speed1D(this.value + delta1d.value, this.unit);
            } else {
                return this;
            }
        } else {
            return this;
        }
    }
    
    public String toString(){
        return "(" + this.value + ") " + this.unit;
    }
}
In [ ]:
Speed speed1 = new Speed1D(10, SpeedUnit.KMH);
Speed speed2 = new Speed1D(20, SpeedUnit.KMH);

Speed speed3 = speed1.add(speed2);
System.out.println(speed3);
In [ ]:
class Speed3D extends Speed {
    protected int valueX;
    protected int valueY;
    protected int valueZ;

    public Speed3D(int x, int y, int z, SpeedUnit unit){
        super(unit);
        this.valueX = x;
        this.valueY = y;
        this.valueZ = z;
    }
    
    public Speed add(Speed delta){
        if(delta instanceof Speed3D){
            Speed3D delta3d = (Speed3D) delta;
            if(this.unit == delta3d.unit){
                return new Speed3D(this.valueX + delta3d.valueX, 
                    this.valueY + delta3d.valueY,
                    this.valueZ + delta3d.valueZ, 
                    this.unit);
            } else {
                return this;
            }
        } else {
            return this;
        }
    }
    
    public String toString(){
        return "(" + this.valueX + "," + this.valueY + "," + this.valueZ + ") " + this.unit;
    }
}
In [ ]:
Speed speed1 = new Speed3D(10, 10, 10, SpeedUnit.KMH);
Speed speed2 = new Speed3D(30, 40, 50, SpeedUnit.KMH);

Speed speed3 = speed1.add(speed2);
System.out.println(speed3);
In [ ]:
class Speed3D extends Speed {
    protected int valueX;
    protected int valueY;
    protected int valueZ;

    public Speed3D(int x, int y, int z, SpeedUnit unit){
        super(unit);
        this.valueX = x;
        this.valueY = y;
        this.valueZ = z;
    }
    
    public Speed add(Speed delta){
        if(delta instanceof Speed1D){
            Speed1D delta1d = (Speed1D) delta;
            if(this.unit == delta1d.unit){
                return new Speed3D(this.valueX + delta1d.value, 
                    this.valueY + delta1d.value,
                    this.valueZ + delta1d.value, 
                    this.unit);
            } else {
                return this;
            }
        } else {
            return this;
        }
    }
    
    public String toString(){
        return "(" + this.valueX + "," + this.valueY + "," + this.valueZ + ") " + this.unit;
    }
}
In [ ]:
Speed speed1 = new Speed3D(10, 10, 10, SpeedUnit.KMH);
Speed speed2 = new Speed1D(30, SpeedUnit.KMH);

Speed speed3 = speed1.add(speed2);
System.out.println(speed3);

Dostęp pakietowy

In [ ]:
//package agh.cs.lecture;

class Speed {
    int value;
    SpeedUnit unit;
    
    public Speed(int value, SpeedUnit unit){
        this.value = value;
        this.unit = unit;
    }
}
In [ ]:
//package agh.cs.lecture;

class SpaceProbe {
    private Speed speed;
    
    public void accelerate(Speed delta){
        if(this.speed.unit == delta.unit){           // dozwolone
            //..
        }
    }
}
In [ ]:
//package com.mycompany;

class SpaceShip {
    private Speed speed;

    public void accelerate(Speed delta){
        if(this.speed.unit == delta.unit){           // niedozwolone!
            //..
        }
    }
}

Podsumowanie modyfikatorów dostępu

  private default protected public
ta sama klasa + + + +
klasa w tym samym pakiecie - + + +
klasa dziedzicząca (z innego pakietu) - - + +
pozostałe klasy - - - +

Metody dostępowe - "gettery"

In [ ]:
class Speed {
    private int value;
    private SpeedUnit unit;
    
    public int getValue(){
        return this.value;
    }
    
    public SpeedUnit getUnit(){
        return this.unit;
    }
}

Dostęp dziedzinowy

In [ ]:
class Speed {
    private int value;
    private SpeedUnit unit;
    
    public int getValueInMS(){
        if(this.unit == SpeedUnit.MS){
            return value;
        } else {
            return convert(this.unit, SpeedUnit.MS, this.value);
        }
    }
    
    public int getValueInKMH(){
        //...
        return 0;
    }
    
    private int convert(SpeedUnit from, SpeedUnit to, int value){
        if(from == SpeedUnit.KMH && to == SpeedUnit.MS) {
            return (int)Math.round(value / 3.6);
        } else {
            //...
            return 0;
        }
    }
}

Metody dostępowe - "settery"

In [ ]:
class Speed {
    private int value;
    private SpeedUnit unit;
    
    public setValue(int value){
        this.value = value;
    }
    
    public setUnit(SpeedUnit unit){
        this.value = convert(this.unit, unit, this.value);
        this.unit = unit;
    }
}

Utrzymywanie jednolitej reprezentacji

In [ ]:
class Speed {
    private int valueInMs;
    private SpeedUnit unit;
    
    public Speed(int value, SpeedUnit unit){
        this.valueInMs = convert(unit, SpeedUnit.MS, value);
        this.unit = unit;
    }
    
    public int getValue(){
        return convert(SpeedUnit.MS, this.unit, this.valueInMs);
    }
    
    public void setValue(int value){
        this.valueInMs = convert(unit, SpeedUnit.MS, value);
    }
    
    public void setUnit(SpeedUnit unit){
        this.unit = unit;
    }
    
    private int convert(SpeedUnit fromUnit, SpeedUnit toUnit, int value){
        //...
        return 0;
    }
}

ValueObject - konwersja

In [ ]:
class Speed {
    private final int value;
    private final SpeedUnit unit;
    
    public Speed(int value, SpeedUnit unit){
        this.value = value;
        this.unit = unit;
    }
    
    public Speed convertToMs(){
        if(this.unit == SpeedUnit.MS){
            return this;
        } else {
            return new Speed(convert(this.unit, SpeedUnit.MS, this.value), 
                             SpeedUnit.MS);
        }
    }
}

Shadowing

In [ ]:
enum SpeedUnit { MS, KMH }
In [ ]:
class Speed {
    private SpeedUnit unit;
    
    public Speed(SpeedUnit unit){
        this.unit = unit;
    }
    
    public SpeedUnit getSuperUnit(){
        return this.unit;
    }
    
    public SpeedUnit getUnit(){
        return this.unit;
    }
}
In [ ]:
class Speed1D extends Speed{
    private SpeedUnit unit; // <-----!
    private int value;
    
    public Speed1D(int value, SpeedUnit unit){
        super(unit);
        this.value = value;
        this.unit = SpeedUnit.KMH; // <-----!
    }
    
    public SpeedUnit getUnit(){
        return this.unit;
    }
}
In [ ]:
Speed1D speed1 = new Speed1D(10, SpeedUnit.MS);
System.out.println(speed1.getUnit());
System.out.println(speed1.getSuperUnit());

In [ ]:
class Speed {
    public SpeedUnit unit;
    
    public Speed(SpeedUnit unit){
        this.unit = unit;
    }
}

class Speed1D extends Speed {
    public SpeedUnit unit; // <-----!
    public int value;
    
    public Speed1D(SpeedUnit unit, int value){
        super(unit);
        this.unit = SpeedUnit.KMH;
        this.value = value;
    }
}

Speed speed1 = new Speed1D(SpeedUnit.MS, 10);
System.out.println(speed1.unit);
Speed1D speed2 = (Speed1D) speed1;
System.out.println(speed2.unit);

Zmienne statyczne

In [ ]:
class Speed {
    private int value;
    private SpeedUnit unit;
    
    private double convert(SpeedUnit from, SpeedUnit to, int value){
        if(from == SpeedUnit.MS && to == SpeedUnit.KMH){
            return value * 3.6;
        } else if(from == SpeedUnit.KMH && to == SpeedUnit.MS){
            return value / 3.6;
        } else {
            //...
            return 0;
        }
    }
}
In [ ]:
class Speed {
    private double ms2kmhRatio = 3.6;
    
    private int value;
    private SpeedUnit unit;
    
    private double convert(SpeedUnit from, SpeedUnit to, int value){
        if(from == SpeedUnit.MS && to == SpeedUnit.KMH){
            return value * ms2kmhRatio;
        } else if(from == SpeedUnit.KMH && to == SpeedUnit.MS){
            return value / ms2kmhRatio;
        } else {
            //...
            return 0;
        }
    }
}

Modyfikator static

In [ ]:
class Speed {
    private static final double ms2kmhRatio = 3.6;
    
    private int value;
    private SpeedUnit unit;
    
    private double convert(SpeedUnit from, SpeedUnit to, int value){
        if(from == SpeedUnit.MS && to == SpeedUnit.KMH){
            return value * ms2kmhRatio;
        } else if(from == SpeedUnit.KMH && to == SpeedUnit.MS){
            return value / ms2kmhRatio;
        } else {
            //...
            return 0;
        }
    }
}

Antywzorzec

In [ ]:
class SpaceShip {
    public static List<SpaceShip> ships = new LinkedList<>();
    
    public SpaceShip(){
        ships.add(this);
    }
}
In [ ]:
class Space {
    private List<SpaceShip> ships = new LinkedList<>();
    
    public SpaceShip createShip(){
        SpaceShip ship = new SpaceShip(this);
        ships.add(ship);
        return ship;
    }
}

class SpaceShip {
    private Space space;
    
    public SpaceShip(Space space){
        this.space = space;
    }
}
In [ ]:
Space thisWorld = new Space();
SpaceShip rocket = thisWorld.createShip();
SpaceShip spaceShuttle = thisWorld.createShip();

Space alienWorld = new Space();
SpaceShip ufo = alienWorld.createShip();